#include <dispatch_server/dispatch_server.h>
#include <string>
#include <sch_common/sch_common.h>



using namespace std;
using namespace moying::communication;
using namespace placeholders;
using namespace moying::sch;

//固定路径数据文件路径
#define CONST_NAV_DATA_PATH "configs/traffic_manager/const_nav_path.data"
#define CONST_NAV_PATH_DATA_VERSION "1.0"
#define VERSION_STR_LEN 10

//返回成功
#define RET_SUCCESS(res) \
    res.mutable_result()->set_code(ReqErrType::kReqSuccess); \
    res.mutable_result()->set_msg("SUCCESS");

//返回失败
#define RET_FAIL(res,code,msg) \
    res.mutable_result()->set_code(code); \
    res.mutable_result()->set_msg(msg);

bool DispatchServer::loadConstNavPathData(){

  string const_nav_data_path = sch_root_path + CONST_NAV_DATA_PATH;
  string all_const_nav_data;
  if( !readFileData(const_nav_data_path,all_const_nav_data) ){
    printf("!!!! load all const nav path data failed\n");
    return false;
  }else{

    const char *p = all_const_nav_data.c_str();
    int size = all_const_nav_data.size();

    //检验数据文件版本号是否与源码一致
    const char* version = p;
    p += VERSION_STR_LEN;
    size -= VERSION_STR_LEN;
    if(strcmp(version,CONST_NAV_PATH_DATA_VERSION) != 0){
        printf("!!!! const nav path data version is invalid, version is %s\n",version);
        return false;
    }else{
        all_const_nav_path_.all_path.clear();
        while(size){
          int data_size = *(int*)p;
          p += 4;

          moying_proto::ConstantPathInfo const_path;
          const_path.ParseFromArray(p,data_size);
          all_const_nav_path_.all_path.emplace_back(const_path);
          p += data_size;
          size -= (4+data_size);
        }
        ostringstream os;
        os<<"load all const nav path data success, count:"<<all_const_nav_path_.all_path.size();
        LOG_DEBUG(os.str());
    }

  }

  return true;
}

bool DispatchServer::saveConstNavPathData(){

  string const_nav_data_path = sch_root_path + CONST_NAV_DATA_PATH;
  static string const_nav_path_md5 = "";

  //保存固定路径数据
  if(!all_const_nav_path_.all_path.empty()){

      int data_size = 0;
      vector<std::string> str_vector;
      
      for(auto const_nav : all_const_nav_path_.all_path){
          string data;
          const_nav.SerializeToString(&data);
          str_vector.emplace_back(data);
          data_size += data.size() + 4;
      }
      

      if(data_size > 0){

        data_size += VERSION_STR_LEN;
        char *buf = new char[data_size];
        char *p = buf;
        strncpy(p,CONST_NAV_PATH_DATA_VERSION,VERSION_STR_LEN);
        p += VERSION_STR_LEN;

        for(auto str : str_vector){

          *(int*)p = str.size();
          p += 4;
          int len = str.size();
          memcpy(p,str.data(),len);
          p += len;

        }

        MD5 md5(string(buf,data_size));
        string md5_str = md5.toStr();

        
        //如果和上一次的数据不一致，写入文件
        if(md5_str.compare(const_nav_path_md5) != 0){
          const_nav_path_md5  = md5_str;
          
          bool ret = writeFileData(const_nav_data_path,string(buf,data_size));
          if(!ret){
            LOG_ERROR("write const nav path data to file failed");
            delete buf;
            return false;
          }
        }
        delete buf;
      }
  }else{

    const_nav_path_md5 = "";
    if(!deleteFile(const_nav_data_path)){
      return false;
    }

  }

  return true;
}

void DispatchServer::handleManageConstNavPathService(const moying_proto::ManageConstantNavPathService_Request& req,
            moying_proto::ManageConstantNavPathService_Response& res){

    std::string ws = req.const_path_info().map_workspace();
    std::string map = req.const_path_info().map_name();
    std::string path_id = req.const_path_info().path_id();
    auto path_data = req.const_path_info().path_point();

    switch(req.op()){
        case DataOpCode::kAddDataOpCode:
        {
            
            if(ws.empty() || map.empty() || path_id.empty() || path_data.empty()){
                RET_FAIL(res,kReqParamErr,"map_workspace or map_name or path_id or path_point filed cannot be empty");
                return;
            }

            std::vector<moying_proto::ConstantPathInfo> const_path_vec;
            if(getConstPath(ws,map,path_id,const_path_vec)){
                RET_FAIL(res,kReqException,"path already exist,map_workspace:"+ws+" map_name:"+map);
                return;
            }

            std::lock_guard<std::recursive_mutex> lock(data_mutex_);
            all_const_nav_path_.all_path.emplace_back(req.const_path_info());

            if(saveConstNavPathData()){
                RET_SUCCESS(res);
            }else{
                RET_FAIL(res,kReqInternalErr,"save constant nav path data failed");
            }
        }
        break;
        case DataOpCode::kDelDataOpCode:
        {
            if(ws.empty() || map.empty() || path_id.empty()){
                RET_FAIL(res,kReqParamErr,"map_workspace or map_name or path_id filed cannot be empty");
                return;
            }

            std::lock_guard<std::recursive_mutex> lock(data_mutex_);
            bool deleted = false;
            for(auto iter=all_const_nav_path_.all_path.begin(); iter != all_const_nav_path_.all_path.end();iter++){
                if(iter->map_workspace().compare(ws) == 0 && iter->map_name().compare(map) == 0 && iter->path_id().compare(path_id) == 0){
                    all_const_nav_path_.all_path.erase(iter);
                    deleted = true;
                    break;
                }
            }
            if(!deleted){
                RET_FAIL(res,kReqParamErr,"can't find specify path, path_id:"+path_id);
                return;
            }

            if(saveConstNavPathData()){
                RET_SUCCESS(res);
            }else{
                RET_FAIL(res,kReqInternalErr,"save constant nav path data failed");
            }
        }
        break;
        case DataOpCode::kSetDataOpCode:
        {

            if(ws.empty() || map.empty() || path_id.empty() || path_data.empty()){
                RET_FAIL(res,kReqParamErr,"map_workspace or map_name or path_id or path_point filed cannot be empty");
                return;
            }

            std::lock_guard<std::recursive_mutex> lock(data_mutex_);
            bool replaced = false;
            for(auto iter=all_const_nav_path_.all_path.begin(); iter != all_const_nav_path_.all_path.end();iter++){
                if(iter->map_workspace().compare(ws) == 0 && iter->map_name().compare(map) == 0 && iter->path_id().compare(path_id) == 0){
                    *iter = req.const_path_info();
                    replaced = true;
                    break;
                }
            }
            if(!replaced){
                RET_FAIL(res,kReqParamErr,"can't find specify path, path_id:"+path_id);
                return;
            }

            if(saveConstNavPathData()){
                RET_SUCCESS(res);
            }else{
                RET_FAIL(res,kReqInternalErr,"save constant nav path data failed");
            }
        }
        break;
        case DataOpCode::kGetDataOpCode:
        {

            // if(ws.empty() || map.empty() || path_id.empty()){
            //     RET_FAIL(res,kReqParamErr,"map_workspace or map_name or path_id or path_point filed cannot be empty");
            //     return;
            // }

            std::vector<moying_proto::ConstantPathInfo> const_path_vec;
            if(!getConstPath(ws,map,path_id,const_path_vec) && !all_const_nav_path_.all_path.empty()){
                RET_FAIL(res,kReqException,"can't get constant nav path,map_workspace:"+ws+" map_name:"+map+" path_id:"+path_id);
                return;
            }else{
                for(auto path : const_path_vec){
                    *res.add_path_info() = path;
                }
                RET_SUCCESS(res);
            }

        }
        break;
        default:
            RET_FAIL(res,kReqParamErr,"op_code is invalid");
        break;
    }
}


bool DispatchServer::getConstPath(std::string map_workspace,std::string map_name,std::string path_id,
    std::vector<moying_proto::ConstantPathInfo>& const_path_vec){


  std::lock_guard<std::recursive_mutex> lock(data_mutex_);
  for(auto path : all_const_nav_path_.all_path){
    if(map_workspace.empty() || path.map_workspace().compare(map_workspace) == 0){
      if( map_name.empty() || path.map_name().compare(map_name) == 0 ){
        if( path_id.empty() || path.path_id().compare(path_id) == 0 ){
          const_path_vec.emplace_back(path);
        }
      }
    }
  }

  return const_path_vec.empty() ? false : true;
}


 